home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Collection of Tools & Utilities
/
Collection of Tools and Utilities.iso
/
batchut
/
rbsetnv1.zip
/
POPEN.C
< prev
next >
Wrap
Text File
|
1990-10-18
|
14KB
|
437 lines
/*
* popen/pclose: simple MS-DOS piping scheme to imitate UNIX pipes
*
* [Obtained from SIMTEL20 as popen.arc - no author name on it
* Contains a lot of debugging code, which I left in.]
* #define DEBUG to get chatty output.
*
* Revision record:
* 08/10/90 RB
* - Added checks on length of generated command line before attempting to run
* - Set O_DENYNONE mode on stdin/stdout before running command
* - Modified naming the pipe file to include PID to guard against nested execution
*
* 03/12/90 RB
* - Changed strsave/strfree to standard strdup/free
* - Added #ifdef SWITCH to force switchar to / before running shell
* - Added set_popen_shell() and set_popen_exec() to select method
*
* 10/5/89 RB
* - Changed check for MKS ksh into explicit check for ksh, as opposed to
* anything other than command.com (I use 4dos)
* - Check for trailing "/" or "\" on $TMP - do not add another
* - Added a simpler version of run() that does not need COMSPEC or getswitch()
* and executes the program directly via spawnvp(). It will work for all but
* .BAT files or internal commands
* - Added my own versions of dup() and dup2()
* - Changed function declarations to prototypes
* - Added close ostdout/ostdin (original left them open, using up handles)
*/
#include <stdio.h>
#include <ctype.h>
#include <alloc.h>
#include <string.h>
#include <errno.h>
#include <setjmp.h>
#include <process.h>
#include <io.h>
#include <dos.h>
#include <fcntl.h>
#include "popen.h"
extern char *getenv( char * );
extern int getswitch(void);
extern int setswitch(char);
#ifndef _NFILE
#define _NFILE OPEN_MAX /* Number of open files */
#endif _NFILE
#define READIT 1 /* Read pipe */
#define WRITEIT 2 /* Write pipe */
#define MAXARGLINE 127 /* max length of an msdos command line */
#define MAX_ARGS 64 /* max number of separate arguments */
#define TRUE 1
#define FALSE 0
static char *prgname[ _NFILE ]; /* program name if write pipe */
static int pipetype[ _NFILE ]; /* 1=read 2=write */
static char *pipename[ _NFILE ]; /* pipe file name */
static int use_shell = TRUE; /* flag for type of exec */
/*
*------------------------------------------------------------------------
* run: Execute command via SHELL or COMSPEC. This version will run .EXE
* .COM .BAT or internal commands of the shell program, also pipelines.
*------------------------------------------------------------------------
*/
static int
runshell( char *command )
{
jmp_buf panic; /* How to recover from errors */
int lineno; /* Line number where panic happened */
char *shell; /* Command processor */
char *s = (char *) NULL; /* Holds the command */
int s_is_malloced = 0; /* True if need to free 's' */
static char *command_com = "COMMAND.COM";
int status; /* Return codes */
char *shellpath; /* Full command processor path */
char *bp; /* Generic string pointer */
char saveswitch; /* Save the switch char */
static char dash_c[ 3 ] = { '?', 'c', '\0' };
if( (lineno = setjmp( panic )) != 0 ) {
int E = errno;
#ifdef DEBUG
fprintf( stderr, "RUN panic on line %d: %d\n", lineno, E );
#endif DEBUG
if( s_is_malloced && (s != (char *) NULL) ) free( s );
errno = E;
return( -1 );
}
if( (s = strdup( command )) == (char *) NULL ) longjmp( panic, __LINE__ );
/* Determine the command processor - use SHELL and then COMSPEC */
if( ((shell = getenv( "SHELL" )) == (char *) NULL) &&
((shell = getenv( "COMSPEC" )) == (char *) NULL) ) shell = command_com;
strupr( shell );
shellpath = shell;
/* Strip off any leading backslash directories */
shell = strrchr( shellpath, '\\' );
if( shell != (char *) NULL ) ++shell;
else shell = shellpath;
/* Strip off any leading slash directories */
bp = strrchr( shell, '/' );
if( bp != (char *) NULL ) shell = ++bp;
if( strstr( shell, "KSH" ) != NULL ) {
/* MKS Shell needs quoted argument */
char *bp;
if( (bp = s = malloc( strlen( command ) + 3 )) == (char *) NULL )
longjmp( panic, __LINE__ );
*bp++ = '\'';
while( (*bp++ = *command++) != '\0' );
*(bp - 1) = '\'';
*bp = '\0';
s_is_malloced = 1;
} else s = command;
saveswitch = dash_c[ 0 ] = (char) getswitch();
#ifdef SWITCH /* some shells will not work without '/' so force it here */
setswitch('/');
dash_c[ 0 ] = '/';
#endif
/* Test the length of the generated command line - if too long, fail */
if (strlen(s) + strlen(shell) + 4 > MAXARGLINE) {
#ifdef DEBUG
fprintf(stderr,"popen: command line too long\n");
#endif DEBUG
errno = E2BIG;
return(-1);
}
/* Run the program */
#ifdef DEBUG
fprintf( stderr, "Running: (%s) %s %s %s\n", shellpath, shell, dash_c, s );
#endif DEBUG
status = spawnl( P_WAIT, shellpath, shell, dash_c, s, (char *) NULL );
if( s_is_malloced ) free( s );
#ifdef SWITCH /* restore switchar if we changed it */
setswitch(saveswitch);
#endif
return( status );
}
/*
*------------------------------------------------------------------------
* run: Execute command directly with spawnvp. This version will only run
* single .EXE or .COM files but is much faster than using COMSPEC
*------------------------------------------------------------------------
*/
/*
* If we know something about the program to be run, we can execute it
* directly instead of calling the shell, thereby saving some time and memory
* This routine added by R. Brittain
*/
static int
runexec( char *command )
{
char *vals[MAX_ARGS]; /* array of pointers to the arguments */
char cmd[MAXARGLINE+1], *s;
int cnt=0, totlen=0, status, i;
/* Copy command and parse argument list. No support for \" in this version */
s = cmd;
strcpy(s,command); /* copy the argument - we will modify this */
while (TRUE) {
s += strspn(s, " \t\n"); /* skip leading whitespace */
if (*s == '\0')
break;
if (cnt >= MAX_ARGS) {
#ifdef DEBUG
fprintf(stderr,"popen: too many arguments in call to exec\n");
#endif DEBUG
return(-1);
}
if (*s == '"') { /* open quote - look for closing quote */
vals[cnt] = ++s; /* quotes are not included in argument */
if ((s = strchr(s, '"')) == NULL)
break;
++cnt;
*s++ = '\0'; /* terminate this argument */
} else {
vals[cnt++] = s; /* start of a new argument */
s += strcspn(s, " \n\t");
if (*s == '\0')
break;
else
*s++ = '\0';
}
}
vals[cnt] = NULL; /* terminate array with a null pointer */
/*
* Test the final parsed arguments against maximum allowable command line
* before executing. If too long, fail.
*/
for (i=0; vals[i] != NULL; i++) {
totlen += strlen(vals[i]) + 1;
if (totlen > MAXARGLINE) {
#ifdef DEBUG
fprintf(stderr,"popen: command line too long\n");
#endif DEBUG
errno = E2BIG;
return(-1);
}
}
#ifdef DEBUG
fprintf( stderr, "Running: %s\n", command );
#endif DEBUG
status = spawnvp( P_WAIT, vals[0], vals);
return( status );
}
/*
*------------------------------------------------------------------------
* uniquepipe: returns a unique file name to use as a pipe
*------------------------------------------------------------------------
*/
static char *
uniquepipe(void)
{
static char name[ 14 ];
static short int num = 0;
(void) sprintf( name, "p%04x%03.3d.tmp", getpid(), num++ );
return( name );
}
/*
*------------------------------------------------------------------------
* resetpipe: Private routine to cancel a pipe
*------------------------------------------------------------------------
*/
static void
resetpipe( int fd )
{
char *bp;
if( (fd >= 0) && (fd < _NFILE) ) {
pipetype[ fd ] = 0;
if( (bp = pipename[ fd ]) != (char *) NULL ) {
(void) unlink( bp );
free( bp );
pipename[ fd ] = (char *) NULL;
}
if( (bp = prgname[ fd ]) != (char *) NULL ) {
free( bp );
prgname[ fd ] = (char *) NULL;
}
}
}
/*
*-------------------------------------------------------------